﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HSDc.RingleFlow.Api.Kernel;
using HSDc.RingleFlow.Kernel.DBPersit.PersitEnum;
using HSDc.RingleFlow.Api.Service;
using HSDc.RingleFlow.Api.Kernel.Org;
using HSDc.RingleFlow.Api.Factory;
using HSDc.RingleFlow.Api;

namespace HSDc.RingleFlow.Kernel.DBPersit.Dao
{
    internal class TaskServiceDao
    {
        internal List<ActivityInstanceDto> RetrieveActivityInstances(string userID)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            List<ActivityInstanceDto> tasks = new List<ActivityInstanceDto>();
            //tasks.AddRange(RetrievePersonalTask(userID, entity));
            tasks.AddRange(RetrieveGroupTask(userID, entity));
            tasks.AddRange(RetrieveDeptTask(userID, entity));
            tasks.AddRange(RetrieveRoleTask(userID, entity));
            return tasks;
        }

        private List<ActivityInstanceDto> RetrievePersonalTask(string userID, RingleFlowEntities entity)
        {
            var tasks = from tk in entity.ActivityInstanceDto
                        where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                   (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                   (tk.ActivityInstance_Assignee.ToLower() == userID.ToLower()) &&
                                   (tk.ActivityInstance_Status == (int)TaskState.Open))
                        select tk;
            return tasks.ToList();
        }

        private List<ActivityInstanceDto> RetrieveGroupTask(string userID, RingleFlowEntities entity)
        {
            var tasks = from tk in entity.ActivityInstanceDto
                        where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                   (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                   (tk.ActivityInstance_Assignee.ToLower().Contains(userID)) &&
                                   (tk.ActivityInstance_Status == (int)TaskState.Open))
                        select tk;
            return tasks.ToList();
        }

        private List<ActivityInstanceDto> RetrieveDeptTask(string userID, RingleFlowEntities entity)
        {
            IWorkflowEngine engine  = WorkflowEngineFactory.GetInstance();
            IOrganisationService orgService = engine.OrgaisationService;
            List<ActivityInstanceDto> resultList = new List<ActivityInstanceDto>();
            IEmp emp = orgService.GetEmployee(userID);
            if (emp.MainDepartment != null)
            {
                var tasks = from tk in entity.ActivityInstanceDto
                            where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                       (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                       (tk.ActivityInstance_Assignee.ToLower()
                                        == ("dept|" + emp.MainDepartment.OrgId).ToLower()) &&
                                       (tk.ActivityInstance_Status == (int)TaskState.Open))
                            select tk;
                resultList.AddRange(tasks.ToList());
            }
            return resultList;
        }

        private List<ActivityInstanceDto> RetrieveRoleTask(string userID, RingleFlowEntities entity)
        {
            IWorkflowEngine engine = WorkflowEngineFactory.GetInstance();
            IOrganisationService orgService = engine.OrgaisationService;
            IEmp emp = orgService.GetEmployee(userID);
            string[] roles = emp.Role.Split(',');
            List<ActivityInstanceDto> returnList = new List<ActivityInstanceDto>();
            foreach (string role in roles)
            {
                var tasks = from tk in entity.ActivityInstanceDto
                            where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                       (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                       (tk.ActivityInstance_Assignee.ToLower()
                                        == ("role|" + role).ToLower()) &&
                                       (tk.ActivityInstance_Status == (int)TaskState.Open))
                            select tk;
                returnList.AddRange(tasks.ToList());
            }
            return returnList;
        }

        internal string RetreiveProcessInstanceIdByGuid(int piId)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var id = (from pi in entity.ProcessInstanceDto
                        where (pi.ProcessInstance_ID == piId)
                        select pi.ProcessInstance_GUID).SingleOrDefault();
            return id;
        }

        internal string RetrieveActivityId(int id)
        {
            return (new RuntimeServiceDao()).RetrieveActivityId(id);
        }

        internal ActivityInstanceDto RetrieveActivityInstance(string userID, string piId)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var task = (from tk in entity.ActivityInstanceDto
                        where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                   (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                   (tk.ProcessInstanceDto.ProcessInstance_GUID == piId) &&
                                   (tk.ActivityInstance_Assignee.ToLower() == userID.ToLower()) &&
                                   (tk.ActivityInstance_Status == (int)TaskState.Open))
                        select tk).SingleOrDefault();
            return task;
        }

        internal ActivityInstanceDto RetrieveActivityInstance(string guid)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var task = (from tk in entity.ActivityInstanceDto
                        where (((tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Suspended) &&
                                   (tk.ProcessInstanceDto.ProcessInstance_Status != (short)ExecutionState.Ended)) &&
                                   (tk.ActivityInstance_GUID.ToLower() == guid.ToLower()) &&
                                   (tk.ActivityInstance_Status == (int)TaskState.Open))
                        select tk).SingleOrDefault();
            return task;
        }

        internal void CompleteActivityInstance(ActivityInstance ai)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            ActivityInstanceDto tmpAiDto = null;
            var aiDto = from ait in entity.ActivityInstanceDto
                        where (ait.ActivityInstance_GUID.ToLower() == ai.Id.ToLower())
                        select ait;
            if (aiDto.ToList().Count > 0)
                tmpAiDto = aiDto.First();
            SaveActivityInstance(ai, entity, tmpAiDto);
            // Commit
            entity.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
        }

        private void SaveActivityInstance(ActivityInstance ai, RingleFlowEntities entity, ActivityInstanceDto aiDto)
        {
            if (aiDto == null)
            {
                RemoveActivityInstance(ai, entity);
                ProcessActivityInstanceHistory(ai, entity);
                if (ai.Variables.Count > 0)
                    SaveActivityInstanceVariablesHistory(ai.Variables, ai.Id, entity);
            }
            else
            {
                ProcessActivityInstance(ai, aiDto);
                if (ai.Variables.Count > 0)
                    SaveActivityInstanceVariables(ai.Variables, ai.Id, entity);
            }
        }

        private void RemoveActivityInstance(ActivityInstance ai, RingleFlowEntities entity)
        {
            if (ai.Variables.Count > 0)
            {
                foreach(string key in ai.Variables.Keys)
                {
                    var variable = from vi in entity.InstanceVariableDto
                                   where ((vi.Instance_GUID == ai.Id) &&
                                              (vi.Variable_Name.ToLower() == key.ToLower()))
                                    select vi;
                    if (variable.ToList().Count > 0)
                        entity.DeleteObject(variable.First());
                }
            }
        }

        private void ProcessActivityInstance(ActivityInstance ai, ActivityInstanceDto aiDto)
        {
            aiDto.ActivityInstance_End = DateTime.Now;
            aiDto.ActivityInstance_Status = (int)((ITask)ai).CurrentState;
            aiDto.ActivityInstance_Assignee = ((ITask)ai).Assignee;
        }

        private void ProcessActivityInstanceHistory(ActivityInstance ai, RingleFlowEntities entity)
        {
            var aihDto = (from aith in entity.ActivityInstanceHistoryDto
                          where (aith.ActivityInstance_GUID.ToLower() == ai.Id.ToLower())
                          select aith).Single();
            aihDto.ActivityInstance_End = DateTime.Now;
            aihDto.ActivityInstance_Status = (int)((ITask)ai).CurrentState;
            aihDto.ActivityInstance_Assignee = ((ITask)ai).Assignee;
        }


        private void SaveActivityInstanceVariables(Dictionary<string, object> variables, string aiId, RingleFlowEntities entity)
        {
            object value = null;
            InstanceVariableDto iv = null;
            foreach (string key in variables.Keys)
            {
                variables.TryGetValue(key, out value);
                var variable = from varDto in entity.InstanceVariableDto
                                where ((varDto.Variable_Name == key) &&
                                           (varDto.Instance_GUID == aiId))
                                select varDto;
                if (variable.ToList().Count == 0)
                    iv = CreateNewVariable(aiId, entity, key, iv);
                else
                    iv = variable.First();

                FillVariableValue(value, iv);
            }
        }

        private void FillVariableValue(object value, InstanceVariableDto variable)
        {
            variable.Variable_Value = value.ToString();
            variable.Variable_Type = (int)VariableType.ActivityInstance;
        }

        private InstanceVariableDto CreateNewVariable(string aiId, RingleFlowEntities entity, string key, InstanceVariableDto variable)
        {
            variable = InstanceVariableDto.CreateInstanceVariableDto(0, key, aiId);
            entity.AddToInstanceVariableDto(variable);
            return variable;
        }

        private void SaveActivityInstanceVariablesHistory(Dictionary<string, object> variables, string aiId, RingleFlowEntities entity)
        {
            object value = null;
            InstanceVariableHistoryDto iv = null;
            foreach (string key in variables.Keys)
            {
                variables.TryGetValue(key, out value);
                var variable = from varDto in entity.InstanceVariableHistoryDto
                                where ((varDto.Variable_Name == key) &&
                                           (varDto.Instance_GUID == aiId))
                                select varDto;
                if (variable.ToList().Count == 0)
                    iv = CreateNewVariableHistory(aiId, entity, key, iv);
                else
                    iv = variable.First();

                FillVariableValueHistory(value, iv);
            }
        }

        private void FillVariableValueHistory(object value, InstanceVariableHistoryDto variable)
        {
            variable.Variable_Value = value.ToString();
            variable.Variable_Type = (int)VariableType.ActivityInstance;
        }

        private InstanceVariableHistoryDto CreateNewVariableHistory(string aiId, RingleFlowEntities entity, string key, InstanceVariableHistoryDto variable)
        {
            variable = InstanceVariableHistoryDto.CreateInstanceVariableHistoryDto(0, key, aiId);
            entity.AddToInstanceVariableHistoryDto(variable);
            return variable;
        }

        internal void SaveActivityInstance(ActivityInstance ai)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            // Go to history and set value
            var aiDto = (from ait in entity.ActivityInstanceDto
                         where (ait.ActivityInstance_GUID.ToLower() == ai.Id.ToLower())
                         select ait).Single();
            ProcessActivityInstance(ai, aiDto);
            if (ai.Variables.Count > 0)
                SaveActivityInstanceVariables(ai.Variables, ai.Id, entity);
            // Commit
            entity.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
        }
    }
}
